
#include "IceCastHttp.h"
#include <curl/curl.h>
#include <QDateTime>

bool isValidSlot(const IceCastSlot * slot)
{
	QDateTime gmtDateTime = QDateTime::currentDateTime().toTimeSpec(Qt::UTC);
	QDateTime estDateTime = gmtDateTime.addSecs(-(5*60*60));
	QDateTime startT = QDateTime::fromString(slot->timeStart, SETTINGS_TIME_FORMAT);
	QDateTime endT = QDateTime::fromString(slot->timeStart, SETTINGS_TIME_FORMAT);

	if (estDateTime.secsTo(startT) <= 0 && estDateTime.secsTo(endT) >=0)
		return true;
	return false;
}

bool isFutureSlot(const IceCastSlot * slot)
{
	QDateTime gmtDateTime = QDateTime::currentDateTime().toTimeSpec(Qt::UTC);
	QDateTime estDateTime = gmtDateTime.addSecs(-(5*60*60));
	QDateTime customT = QDateTime::fromString(slot->timeStart, SETTINGS_TIME_FORMAT);

	if (estDateTime.secsTo(customT) > 0)
		return true;
	return false;
}

bool isAvailableSlot(const IceCastSlot * slot)
{
	if (slot->testmode)
		return true;

	QDateTime gmtDateTime = QDateTime::currentDateTime().toTimeSpec(Qt::UTC);
	QDateTime estDateTime = gmtDateTime.addSecs(-(5*60*60));
	QDateTime customT = QDateTime::fromString(slot->timeStart, SETTINGS_TIME_FORMAT);

	if (estDateTime.secsTo(customT) > 0 && estDateTime.secsTo(customT) < 45 * 60)
		return true;

	return false;
}

int compareSlot(const IceCastSlot * slot1, const IceCastSlot * slot2)
{
	QDateTime time1 = QDateTime::fromString(slot1->timeStart, SETTINGS_TIME_FORMAT);
	QDateTime time2 = QDateTime::fromString(slot2->timeStart, SETTINGS_TIME_FORMAT);

	return time2.secsTo(time1);
}

IceCastHttp::IceCastHttp(const char * baseURL) : 
	m_strRetCode(0), m_strBasedURL(0), m_strPass(0), m_strUser(0), m_strToken(0), m_nSlotCount(0), m_lpConnection(0), m_lpSlots(0)
{
	if (baseURL)
		STR_CPY(m_strBasedURL, baseURL);
	m_lpConnection = new HttpConnection();
}

IceCastHttp::~IceCastHttp()
{
	delete m_lpConnection;
	STR_DEL(m_strBasedURL);
	STR_DEL(m_strUser);
	STR_DEL(m_strPass);
	STR_DEL(m_strToken);
	STR_DEL(m_strRetCode);

	clearSlotsInfo();
}

void IceCastHttp::addSlotInfo(IceCastSlot *info)
{
	if (0 == m_nSlotCount)
		m_lpSlots = info;
	else
	{
		info->next = m_lpSlots;
		m_lpSlots = info;
	}
	m_nSlotCount++;
}

void IceCastHttp::clearSlotsInfo()
{
	m_nSlotCount = 0;
	while (m_lpSlots)
	{
		IceCastSlot *tmp = m_lpSlots;
		m_lpSlots = m_lpSlots->next;

		STR_DEL(tmp->hash);
		STR_DEL(tmp->timeStart);
		STR_DEL(tmp->timeEnd);
		STR_DEL(tmp->custLocation);
		STR_DEL(tmp->custTimeZone);
		STR_DEL(tmp->customer_name);
		STR_DEL(tmp->provider_name);
		STR_DEL(tmp->host);
		STR_DEL(tmp->user);
		STR_DEL(tmp->pass);
		STR_DEL(tmp->mount);
		free(tmp);
	}
}

const char * IceCastHttp::retCode()
{
	return m_strRetCode;
}

void IceCastHttp::setBasedURL(const char * url)
{
	STR_CPY(m_strBasedURL, url);
}

const char * IceCastHttp::basedURL()
{
	return m_strBasedURL;
}

const char * IceCastHttp::user()
{
	return m_strUser;
}

const char * IceCastHttp::pass()
{
	return m_strPass;
}

const char * IceCastHttp::token()
{
	return m_strToken;
}

const int IceCastHttp::login(const char *user, const char *pass)
{
	if (0 == m_strBasedURL)
		return 1;

	const char *szFormat = "%s/ondemandapi?call=auth&username=%s&password=%s";
	char * url = (char*)malloc(strlen(szFormat) + strlen(m_strBasedURL) + strlen(user) + strlen(pass));
	sprintf(url, szFormat, m_strBasedURL, user, pass);
	int nRet = m_lpConnection->requestGet(url);
	free(url);

	if (nRet)
		return 2;

	nRet = parseLogin();
	if (nRet)
		return nRet + 2;

	nRet = refreshSlot();
	if (nRet)
		return nRet + 20;

	return 0;
}

const int IceCastHttp::parseLogin()
{
	const char * strLoginRes = m_lpConnection->response();

	STR_DEL(m_strRetCode);
	m_strRetCode = findSubStrInStr(strLoginRes, TAG_CODE_BEGIN, TAG_CODE_END);
	if (m_strRetCode && strcmp(m_strRetCode, MSG_HTTP_ERROR) == 0)
		return 1;

	STR_DEL(m_strToken);
	m_strToken = findSubStrInStr(strLoginRes, TAG_TOKEN_BEGIN, TAG_TOKEN_END);

	return 0;
}

const int IceCastHttp::refreshSlot()
{
	if (0 == m_strBasedURL)
		return 1;

	if (0 == m_strToken)
		return 2;

	const char *szFormat = "%s/ondemandapi?call=getslots&token=%s";
	char * url = (char*)malloc(strlen(szFormat) + strlen(m_strBasedURL) + strlen(m_strToken));
	sprintf(url, szFormat, m_strBasedURL, m_strToken);
	int nRet = m_lpConnection->requestGet(url);
	free(url);

	if (nRet)
		return 3;

	nRet = parseGetSlots();
	if (nRet)
		return nRet + 3;

	return 0;
}

const int IceCastHttp::parseGetSlots()
{
	const char * strLoginRes = m_lpConnection->response();

	STR_DEL(m_strRetCode);
	m_strRetCode = findSubStrInStr(strLoginRes, TAG_CODE_BEGIN, TAG_CODE_END);
	if (m_strRetCode && strcmp(m_strRetCode, MSG_HTTP_ERROR) == 0)
		return 1;

	STR_DEL(m_strToken);
	m_strToken = findSubStrInStr(strLoginRes, TAG_TOKEN_BEGIN, TAG_TOKEN_END);

	clearSlotsInfo();
	char *lpTemp = findSubStrInStr(strLoginRes, TAG_SLOT_BEGIN, TAG_SLOT_END, 1);
	while (lpTemp)
	{
		IceCastSlot *slot = (IceCastSlot*)malloc(sizeof(IceCastSlot));
		memset(slot, 0, sizeof(IceCastSlot));
		// sid
		char *tmp = findSubStrInStr(lpTemp, "<sid>", "</sid>");
		slot->id = atoi(tmp);
		STR_DEL(tmp);
		// mode
		tmp = findSubStrInStr(lpTemp, "<test_mode>", "</test_mode>");
		slot->testmode = atoi(tmp);
		STR_DEL(tmp);
		// available
		tmp = findSubStrInStr(lpTemp, "<available>", "</available>");
		slot->available = atoi(tmp);
		STR_DEL(tmp);
		// customer id
		tmp = findSubStrInStr(lpTemp, "<customerid>", "</customerid>");
		slot->customerid = atoi(tmp);
		STR_DEL(tmp);
		// provider id
		tmp = findSubStrInStr(lpTemp, "<providerid>", "</providerid>");
		slot->providerid = atoi(tmp);
		STR_DEL(tmp);
		// hash
		slot->hash = findSubStrInStr(lpTemp, "<hash>", "</hash>");
		// time start
		slot->timeStart = findSubStrInStr(lpTemp, "<timestart>", "</timestart>");
		// time end
		slot->timeEnd = findSubStrInStr(lpTemp, "<timeend>", "</timeend>");
		// customer name
		slot->customer_name = findSubStrInStr(lpTemp, "<customer_name>", "</customer_name>");
		// provider name
		slot->provider_name = findSubStrInStr(lpTemp, "<provider_name>", "</provider_name>");
		// customer timezone
		slot->custTimeZone = findSubStrInStr(lpTemp, "<customer_timezone>", "</customer_timezone>");
		// customer timezone
		slot->custLocation = findSubStrInStr(lpTemp, "<customer_country>", "</customer_country>");

		addSlotInfo(slot);
		STR_DEL(lpTemp);
		lpTemp = findSubStrInStr(strLoginRes, TAG_SLOT_BEGIN, TAG_SLOT_END, 1);
	}

	return 0;
}

char * IceCastHttp::findSubStrInStr(const char * data, const char * begin, const char * end, int remove)
{
	const char * posBegin = strstr(data, begin);
	if (posBegin)
	{
		if (remove)
			memset((char*)posBegin, '#', strlen(begin));
		posBegin = posBegin + strlen(begin);
		const char * posEnd = strstr(posBegin, end);
		if (posEnd)
		{
			if (remove)
				memset((char*)posEnd, '#', strlen(end));
			int lenght = (long)posEnd - (long)posBegin;
			char * sRet = (char*)malloc(lenght + 1);
			char * tmp = sRet;
			while (posBegin != posEnd)
				*tmp++ = *posBegin++;
			*tmp = 0;
			return sRet;
		}
	}
	return 0;
}

const int IceCastHttp::slotCount()
{
	return m_nSlotCount;
}

const IceCastSlot * IceCastHttp::slot(int idx)
{
	int nCount = 0;
	IceCastSlot *tmp = m_lpSlots;
	while (tmp)
	{
		if (nCount == idx)
			return tmp;
		tmp = tmp->next;
		nCount++;
	}

	return 0;
}

const IceCastSlot * IceCastHttp::slotById(int id)
{
	IceCastSlot *tmp = m_lpSlots;
	while (tmp)
	{
		if (tmp->id == id)
			return tmp;
		tmp = tmp->next;
	}

	return 0;
}

const int IceCastHttp::initStream(IceCastSlot *slot)
{
	if (0 == m_strBasedURL)
		return 1;

	if (0 == m_strToken)
		return 2;
	
	if (0 == slot)
		return 3;

	const char *szFormat = "%s/ondemandapi?call=initstream&token=%s&sid=%d";
	char * url = (char*)malloc(strlen(szFormat) + strlen(m_strBasedURL) + strlen(m_strToken) + 1);
	sprintf(url, szFormat, m_strBasedURL, m_strToken, slot->id);
	int nRet = m_lpConnection->requestGet(url);
	free(url);

	if (nRet)
		return 3;

	nRet = parseGetIcecastInfo(slot);
	if (nRet)
		return nRet + 3;

	return 0;
}

const int IceCastHttp::freeStream(IceCastSlot *slot)
{
	if (0 == m_strBasedURL)
		return 1;

	if (0 == m_strToken)
		return 2;

	if (0 == slot)
		return 3;

	const char *szFormat = "%s/ondemandapi?call=freestream&token=%s&sid=%d";
	char * url = (char*)malloc(strlen(szFormat) + strlen(m_strBasedURL) + strlen(m_strToken) + 1);
	sprintf(url, szFormat, m_strBasedURL, m_strToken, slot->id);
	int nRet = m_lpConnection->requestGet(url);
	free(url);

	if (nRet)
		return 3;

	const char * strLoginRes = m_lpConnection->response();
	STR_DEL(m_strToken);
	m_strToken = findSubStrInStr(strLoginRes, TAG_TOKEN_BEGIN, TAG_TOKEN_END);

	STR_DEL(slot->host);
	STR_DEL(slot->user);
	STR_DEL(slot->pass);
	STR_DEL(slot->mount);
	slot->port = 0;

	return 0;
}

const int IceCastHttp::parseGetIcecastInfo(IceCastSlot *slot)
{
	const char * strLoginRes = m_lpConnection->response();

	STR_DEL(m_strRetCode);
	m_strRetCode = findSubStrInStr(strLoginRes, TAG_CODE_BEGIN, TAG_CODE_END);
	if (m_strRetCode && strcmp(m_strRetCode, MSG_HTTP_ERROR) == 0)
		return 1;

	STR_DEL(m_strToken);
	m_strToken = findSubStrInStr(strLoginRes, TAG_TOKEN_BEGIN, TAG_TOKEN_END);

	STR_DEL(slot->host);
	slot->host = findSubStrInStr(strLoginRes, "<server>", "</server>");

	STR_DEL(slot->user);
	slot->user = findSubStrInStr(strLoginRes, "<username>", "</username>");

	STR_DEL(slot->pass);
	slot->pass = findSubStrInStr(strLoginRes, "<password>", "</password>");

	STR_DEL(slot->mount);
	slot->mount = findSubStrInStr(strLoginRes, "<mountpoint>", "</mountpoint>");

	char * port = findSubStrInStr(strLoginRes, "<port>", "</port>");
	if (port)
	{
		slot->port = atoi(port);
		STR_DEL(port);
	}

	if (!slot->host || !slot->user || !slot->pass || !slot->mount || !slot->port)
		return 1;

	return 0;
}

const IceCastSlot * IceCastHttp::currentSlot()
{
	IceCastSlot *tmp = m_lpSlots;
	while (tmp)
	{
		if (isValidSlot(tmp))
			return tmp;
		tmp = tmp->next;
	}

	return 0;
}

const IceCastSlot * IceCastHttp::nextSlot()
{
	IceCastSlot *tmp = m_lpSlots;
	IceCastSlot *ret = 0;
	while (tmp)
	{
		if (isFutureSlot(tmp))
		{
			if (!ret || compareSlot(tmp, ret) < 0)
				ret = tmp;
		}
		tmp = tmp->next;
	}

	return ret;
}

IceCastSlot * IceCastHttp::availableSlots()
{
	IceCastSlot *ret = 0;
	IceCastSlot *tmp = m_lpSlots;
	while (tmp)
	{
		if (isAvailableSlot(tmp))
		{
			if (0 == ret)
			{
				CLONE_SLOT_INFO(tmp, ret);
			}
			else
			{
				IceCastSlot *ics = 0;
				CLONE_SLOT_INFO(tmp, ics);
				ics->next = ret;
				ret = ics;
			}
		}
		tmp = tmp->next;
	}

	return ret;
}

